#include "png.h"
#include "../texture.h"
#include "../../library/libpng/png.h"
#include <stdlib.h>

namespace sleek
{
    namespace texture
    {
        texture* LoadTextureFromPng(core::os::stringc file)
        {
            png_byte magic[8];
            png_structp png_ptr;
            png_infop info_ptr;
            int bit_depth = 0, color_type = 0;
            FILE *fp = 0;
            png_bytep *row_pointers = 0;
            png_uint_32 w = 0, h = 0;
            GLubyte *texels = 0;

            fp = fopen (file.c_str(), "rb");
            if(!fp) { printf("error: couldn't open \"%s\"!\n", file.c_str()); return 0; }
            fread (magic, 1, sizeof (magic), fp);
            if (!png_check_sig(magic, sizeof (magic)))
            {
                fprintf (stderr, "error: \"%s\" is not a valid PNG image!\n",file.c_str());
                fclose (fp);
                return 0;
            }
            printf("Load texture: \"%s\"\n", file.c_str());
            png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
            if(!png_ptr){ fclose (fp); return 0; }
            info_ptr = png_create_info_struct (png_ptr);
            if (!info_ptr || setjmp(png_jmpbuf (png_ptr)))
            {
                fclose (fp);
                png_destroy_read_struct (&png_ptr, &info_ptr, 0);
                if (row_pointers)
                free (row_pointers);
                return 0;
            }
            png_init_io (png_ptr, fp);
            png_set_sig_bytes (png_ptr, sizeof (magic));
            png_read_info (png_ptr, info_ptr);

            bit_depth = png_get_bit_depth(png_ptr, info_ptr);
            color_type = png_get_color_type(png_ptr, info_ptr);

            if(color_type == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(png_ptr);
            if(bit_depth < 8)
            {
                if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_expand_gray_1_2_4_to_8(png_ptr);
                else png_set_packing(png_ptr);
            }
            if(png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) png_set_tRNS_to_alpha(png_ptr);
            if(bit_depth == 16) png_set_strip_16(png_ptr);
            if(color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png_ptr);

            //! internal png format change to bgr/rgb
            png_set_bgr(png_ptr);

            png_read_update_info (png_ptr, info_ptr);
            png_get_IHDR (png_ptr, info_ptr, &w, &h, &bit_depth, &color_type, 0, 0, 0);

            int internalFormat = 0;
            if(color_type == PNG_COLOR_TYPE_GRAY)       internalFormat = TXFMT_LUMINANCE;
            if(color_type == PNG_COLOR_TYPE_GRAY_ALPHA) internalFormat = TXFMT_LUMINANCE_ALPHA;
            if(color_type == PNG_COLOR_TYPE_RGB)        internalFormat = TXFMT_RGB;
            if(color_type == PNG_COLOR_TYPE_RGB_ALPHA)  internalFormat = TXFMT_RGBA;

            texels = (GLubyte*)malloc(sizeof(GLubyte)*w*h*internalFormat);
            row_pointers = new png_bytep[h];

            // vertival flip or not ? comment = not
            //for(register unsigned int i = 0; i < h; ++i) row_pointers[i] = (png_bytep)(texels+(i*w*internalFormat));
            for(register unsigned int i = 0; i < h; ++i) row_pointers[i] = (png_bytep)(texels+((h-(i+1))*w*internalFormat));

            png_read_image (png_ptr, row_pointers);
            png_read_end (png_ptr, 0);
            png_destroy_read_struct (&png_ptr, &info_ptr, 0);
            delete row_pointers;
            fclose(fp);

            texture *tmp = new texture(core::math::vector2du(w, h), (TextureFormat)internalFormat);
            tmp->buffer = (core::u8*)texels;

            tmp->flipVertical();
            tmp->GenerateGlTexture();

            return tmp;
        }
        bool WriteTextureToPng(texture*,core::os::stringc a)
        {
            FILE *fp;
            png_structp png_ptr;
            png_infop info_ptr;
            png_colorp palette;

            /* Open the file */
            fp = fopen(a.c_str(), "wb");
            if(!fp) return false;

            png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);

            if(!png_ptr){ fclose(fp); return false; }
            info_ptr = png_create_info_struct(png_ptr);
            if(!info_ptr || setjmp(png_jmpbuf(png_ptr)))
            {
                fclose(fp);
                png_destroy_write_struct(&png_ptr, &info_ptr);
                return false;
            }
            png_write_end(png_ptr, info_ptr);
            png_destroy_write_struct(&png_ptr, &info_ptr);

            fclose(fp);
            return true;
        }
        bool IsTextureFormatPng(core::os::stringc file)
        {
            FILE *fp;
            png_byte magic[8];

            if(!(fp = fopen (file.c_str(), "rb"))) return false;
            fread (magic, 1, sizeof (magic), fp);
            if(!png_check_sig (magic, sizeof (magic))){ fclose (fp); return false; }
            fclose (fp);
            return true;
        }
    }
}
